<?php

\routes\api.php
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:api');


概括的说，使用 \App\User 模型类查找数据，要求 prefix_users 表存在，或者在模型类中指定一个表，比如 
protected $table = 'user';就是查找 prefix_user 表了。
要求表中必须有一个字段 api_token。
请求这个路由时，需要有一个参数，比如 url 中 api_token=112233;
查询时，并没有对该字段进行加解密的操作，是直接查询。
所以，就简单多了。



通过分析路由的流程，我们知道每一个路由就是一个 Route。
\vendor\laravel\framework\src\Illuminate\Routing\Route.php
在 Route 类中有方法 middleware()，意味着每个路由都可以有自己的 middleware。

    public function middleware($middleware = null)
    {
        var_dump($middleware);exit;// 对于此路由，正是 auth:api 字符串；
        if (is_null($middleware)) {
            return (array) Arr::get($this->action, 'middleware', []);
        }

        if (is_string($middleware)) {
            $middleware = func_get_args();
        }
        // var_dump($this->action['middleware']);exit; 字符串 api；
        $this->action['middleware'] = array_merge(
            (array) Arr::get($this->action, 'middleware', []), $middleware
        );
        // var_dump($this->action['middleware']);exit;
        // 数组：array(2) { [0]=> string(3) "api" [1]=> string(8) "auth:api" }

        return $this;
    }

那么 auth:api 怎么理解呢？
    简单的说，auth 被解析成了对应的类，api 作为参数。
参考 response。
这里只列出和分析该路由相关的核心代码：
\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();
        // 上面是框架的整体加载；

        // 下面就是响应当前路由的逻辑；
        // Illuminate\Routing\Pipeline；
        return (new Pipeline($this->app))
                    ->send($request)// 返回 Pipeline 实例本身；
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)// 返回 Pipeline 实例本身；
                    ->then($this->dispatchToRouter());// 返回的是 Illuminate\Http\Response 的实例；
    }
重点就是其中的 ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)；
找到这个方法：
    public function through($pipes)
    {
        var_dump($pipes);echo "<br/>";
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();

        return $this;
    }
访问 http://laravel.jm.cm/api/user 时，这里的打印结果是：
array(1) { [0]=> string(61) "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode" } 
array(3) { [0]=> string(51) "Illuminate\Routing\Middleware\ThrottleRequests:60,1" [1]=> string(43) "Illuminate\Auth\Middleware\Authenticate:api" [2]=> string(48) "Illuminate\Routing\Middleware\SubstituteBindings" } 
这里和 \app\Http\Kernel.php 中的
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    ];
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];
是对应的。额外多了一个 [1]=> string(43) "Illuminate\Auth\Middleware\Authenticate:api"。这正是 auth:api 的东西。
经过测试，本路由去掉 ->middleware('auth:api')，如下：
Route::get('/user', function (Request $request) {
    return $request->user();
});
那么打印结果如下：
array(1) { [0]=> string(61) "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode" } 
array(2) { [0]=> string(51) "Illuminate\Routing\Middleware\ThrottleRequests:60,1" [1]=> string(48) "Illuminate\Routing\Middleware\SubstituteBindings" } 
完全和 \app\Http\Kernel.php 中的两个数组一致。

这表明，auth:api 被解析成了 Illuminate\Auth\Middleware\Authenticate:api。


\app\Http\Kernel.php 中还有一个数组：
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];
经过测试，就是映射的关系，比如api 路由的中间件
        'api' => [
            'throttle:60,1',
            'bindings',
        ],
bindings 就被解析为了 Illuminate\Routing\Middleware\SubstituteBindings。
如果注释 $routeMiddleware 中的 bindings 这一行，那么上面的打印结果就成了：
array(2) { [0]=> string(51) "Illuminate\Routing\Middleware\ThrottleRequests:60,1" [1]=> string(48) "bindings" } 
可以看到就是自己。显然就不对了。
其他的 throttle 和 auth 也是如此。


api 做什么了？
\vendor\laravel\framework\src\Illuminate\Routing\Pipeline.php
    protected function getSlice()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                try {
                    $slice = parent::getSlice();// 调用父类的方法；
                    $callable = $slice($stack, $pipe);

                    return $callable($passable);
                } catch (Exception $e) {
                    return $this->handleException($passable, $e);
                } catch (Throwable $e) {
                    return $this->handleException($passable, new FatalThrowableError($e));
                }
            };
        };
    }
\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php 有方法
    protected function getSlice()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                if ($pipe instanceof Closure) {
                    // If the pipe is an instance of a Closure, we will just call it directly but
                    // otherwise we'll resolve the pipes out of the container and call it with
                    // the appropriate method and arguments, returning the results back out.
                    return $pipe($passable, $stack);
                } elseif (! is_object($pipe)) {
                    var_dump($pipe);echo "$--pipe<br/>";// string(43) "Illuminate\Auth\Middleware\Authenticate:api" $--pipe
                    list($name, $parameters) = $this->parsePipeString($pipe);
                    var_dump($parameters);echo "$--parameters<br/>";// array(1) { [0]=> string(3) "api" } $--parameters

                    // If the pipe is a string we will parse the string and resolve the class out
                    // of the dependency injection container. We can then build a callable and
                    // execute the pipe function giving in the parameters that are required.
                    var_dump($name);echo "$--name<br/>";// string(39) "Illuminate\Auth\Middleware\Authenticate" $--name
                    $pipe = $this->getContainer()->make($name);

                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    // If the pipe is already an object we'll just make a callable and pass it to
                    // the pipe as-is. There is no need to do any extra parsing and formatting
                    // since the object we're given was already a fully instantiated object.
                    $parameters = [$passable, $stack];
                }

                var_dump($this->method);echo "$--this->method<br/>";// string(6) "handle" $--this->method

                // 下面这里就是 $pipe->handle('api');
                // 所以，auth:api 的api 就是在这里使用的；
                return $pipe->{$this->method}(...$parameters);
            };
        };
    }
在 elseif 中
$pipe = $this->getContainer()->make($name);
实例化 Illuminate\Auth\Middleware\Authenticate。





\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php
    use Illuminate\Contracts\Auth\Factory as Auth;
    public function __construct(Auth $auth)
    {
        // var_dump('Authenticate __construct');
        // var_dump($auth);// object(Illuminate\Auth\AuthManager)#134 (5)
        $this->auth = $auth;
    }
    public function handle($request, Closure $next, ...$guards)
    {
        // var_dump($guards);echo "$--guards<br/>";// array(1) { [0]=> string(3) "api" } $--guards
        $this->authenticate($guards);

        return $next($request);
    }
    protected function authenticate(array $guards)
    {
        // var_dump($guards);echo "$--guards<br/>";// array(1) { [0]=> string(3) "api" } $--guards
        if (empty($guards)) {
            return $this->auth->authenticate();
        }

        foreach ($guards as $guard) {
            // $this->auth->guard($guard) 就是 \vendor\laravel\framework\src\Illuminate\Auth\TokenGuard.php；
            // check() 方法则不在 TokenGuard 内，而是 TokenGuard 内的 use GuardHelpers;
            if ($this->auth->guard($guard)->check()) {
                return $this->auth->shouldUse($guard);
            }
        }

        throw new AuthenticationException('Unauthenticated.', $guards);
    }

\vendor\laravel\framework\src\Illuminate\Auth\AuthManager.php
    public function guard($name = null)
    {
        var_dump($name);echo "$--name<br/>";// string(3) "api" $--name
        $name = $name ?: $this->getDefaultDriver();

        var_dump($this->guards);// array(0) { }
        //var_dump($this->guards['api']);// 注意：由于是空数组，所以访问 api 会报错，导致不能继续往下走了。

        return isset($this->guards[$name])
                    ? $this->guards[$name]
                    : $this->guards[$name] = $this->resolve($name);
    }
    protected function resolve($name)
    {
        var_dump($name);echo "$--name<br/>";// string(3) "api" $--name
        $config = $this->getConfig($name);
        var_dump($config);echo "$--config<br/>";
        // array(2) { ["driver"]=> string(5) "token" ["provider"]=> string(5) "users" } $--config
        // \config\auth.php 中
            'guards' => [
                'web' => [
                    'driver' => 'session',
                    'provider' => 'users',
                ],

                'api' => [
                    'driver' => 'token',
                    'provider' => 'users',
                ],
            ],
        的 api 部分。经过测试，修改 value，确实发生变化了。

        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($name, $config);
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
        // createTokenDriver;

        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

        throw new InvalidArgumentException("Auth guard driver [{$name}] is not defined.");
    }
    public function createTokenDriver($name, $config)
    {
        var_dump($name);echo "createTokenDriver $--name<br/>";// string(3) "api" createTokenDriver $--name
        // The token guard implements a basic API token based guard implementation
        // that takes an API token field from the request and matches it to the
        // user in the database or another persistence layer where users are.
        $guard = new TokenGuard(
            $this->createUserProvider($config['provider']),
            $this->app['request']
        );

        $this->app->refresh('request', $guard, 'setRequest');

        return $guard;
    }
其中
        $guard = new TokenGuard(
            $this->createUserProvider($config['provider']),
            $this->app['request']
        );
的 $this->createUserProvider($config['provider']), 来自 use CreatesUserProviders;

\vendor\laravel\framework\src\Illuminate\Auth\CreatesUserProviders.php
    public function createUserProvider($provider)
    {
        var_dump($provider);echo "createUserProvider $--provider<br/>";// string(5) "users" createUserProvider $--provider
        $config = $this->app['config']['auth.providers.'.$provider];
        // 在 \config\auth.php 中存在：
            'providers' => [
                'users' => [
                    'driver' => 'eloquent',
                    'model' => App\User::class,
                ],

                // 'users' => [
                //     'driver' => 'database',
                //     'table' => 'users',
                // ],
            ],

        所以 $config = [
                    'driver' => 'eloquent',
                    'model' => App\User::class,
                ];
        
        var_dump($this->customProviderCreators);echo "createUserProvider $--customProviderCreators<br/>";
        // array(0) { } createUserProvider $--customProviderCreators
        // 空数组，所以不会执行 if。
        
        if (isset($this->customProviderCreators[$config['driver']])) {
            return call_user_func(
                $this->customProviderCreators[$config['driver']], $this->app, $config
            );
        }

        switch ($config['driver']) {
            case 'database':
                return $this->createDatabaseProvider($config);
            case 'eloquent':// 默认情况下是执行这里；
                return $this->createEloquentProvider($config);
            default:
                throw new InvalidArgumentException("Authentication user provider [{$config['driver']}] is not defined.");
        }
    }
    protected function createEloquentProvider($config)
    {
        return new EloquentUserProvider($this->app['hash'], $config['model']);
    }


\vendor\laravel\framework\src\Illuminate\Auth\GuardHelpers.php
    public function check()
    {
        return ! is_null($this->user());
    }

$this->user() 在 \vendor\laravel\framework\src\Illuminate\Auth\TokenGuard.php
    public function user()
    {
        // If we've already retrieved the user for the current request we can just
        // return it back immediately. We do not want to fetch the user data on
        // every call to this method because that would be tremendously slow.
        if (! is_null($this->user)) {
            return $this->user;
        }

        $user = null;

        $token = $this->getTokenForRequest();

        if (! empty($token)) {
            $user = $this->provider->retrieveByCredentials(
                [$this->storageKey => $token]
            );
        }

        return $this->user = $user;
    }
    public function getTokenForRequest()
    {
        
        $token = $this->request->query($this->inputKey);
        var_dump($token);echo "TokenGuard getTokenForRequest $--token<br/>";
        // URL 中的参数 api_token 的值；

        if (empty($token)) {
            // 如果是 get 请求，那么就是从 url 中取参数 api_token；
            // 如果是 post 请求，则优先从 post 中取参数，post 中没有则从 URL 中取参数；
            $token = $this->request->input($this->inputKey);
        }

        if (empty($token)) {
            // 如果还没有，则从请求头中取参数 Authorization；
            $token = $this->request->bearerToken();

            在 \vendor\laravel\framework\src\Illuminate\Http\Request.php
                public function bearerToken()
                {
                    $header = $this->header('Authorization', '');

                    if (Str::startsWith($header, 'Bearer ')) {
                        return Str::substr($header, 7);
                    }
                }
        }

        if (empty($token)) {
            $token = $this->request->getPassword();

            在 \vendor\symfony\http-foundation\Request.php
                public function getPassword()
                {
                    return $this->headers->get('PHP_AUTH_PW');
                }
        }

        return $token;
    }












